import datetime
import json
import logging
import math
import random
import threading
import time
import uuid

import requests

from common.redis_helper import RedisHelper

logger = logging.getLogger('log')


class SpiderProxy(object):
    _instance_lock = threading.Lock()
    lock = threading.Lock()

    channels = ['expedia', 'agoda', 'accor', 'rila', 'ihg', 'booking', 'marriott', 'hotels']

    def __init__(self):
        self.proxy_list = {
            'expedia': [], 'agoda': [], 'accor': [], 'rila': [], 'ihg': [], 'booking': [], 'marriott': [], 'hotels': []
        }
        self.redis_helper = RedisHelper()
        self.proxy_url = "http://http.tiqu.qingjuhe.cn/getip?num=50&type=2&pack=48759&port=11&ts=1&lb=1&pb=45&regions="

    def __new__(cls, *args, **kwargs):
        if not hasattr(SpiderProxy, "_instance"):
            with SpiderProxy._instance_lock:
                if not hasattr(SpiderProxy, "_instance"):
                    # 类加括号就回去执行__new__方法，__new__方法会创建一个类实例：Singleton()
                    SpiderProxy._instance = object.__new__(cls, *args,
                                                           **kwargs)  # 继承object类的__new__方法，类去调用方法，说明是函数，要手动传cls
        return SpiderProxy._instance  # obj1

    def get_tai_yang_proxy(self, channel):
        self.lock.acquire()
        try:
            ip_json = self.redis_helper.get_string("ip_list", None)
            if ip_json is not None:
                self.proxy_list[channel] = json.loads(ip_json)
            if len(self.proxy_list[channel]) < 20:
                logger.error("开始获取代理IP   渠道为 = " + channel + " 大小 = " + str(len(self.proxy_list[channel])))
                is_need_request = False
                last_request_time = self.redis_helper.get_string("ip_last_request_time", None)
                if last_request_time is None:
                    is_need_request = True
                else:
                    last_date_time = datetime.datetime.strptime(last_request_time, '%Y-%m-%d %H:%M:%S')
                    now_time = datetime.datetime.now()
                    if now_time.timestamp() - last_date_time.timestamp() >= 60:
                        is_need_request = True
                if is_need_request:
                    logger.error("真的要去获取代理IP了")
                    results = requests.get(self.proxy_url).json()
                    now_time = datetime.datetime.now()
                    request_time = now_time.strftime("%Y-%m-%d %H:%M:%S")
                    self.redis_helper.set_string("ip_last_request_time", request_time)
                    if results.get('code', 1) == 0:
                        proxy_ips = results.get('data', [])
                        for ip in proxy_ips:
                            ip['use_count'] = 0
                        for c in self.channels:
                            self.proxy_list[c].extend(proxy_ips)
                        self.redis_helper.set_string("ip_list", json.dumps(proxy_ips, ensure_ascii=False))
        except Exception as e:
            logger.error("获取IP出错：{}".format(e))
        self.lock.release()

    def get_valid_ip(self, channel):
        result_ip = None
        if len(self.proxy_list[channel]) < 20:
            self.get_tai_yang_proxy(channel)
        if len(self.proxy_list[channel]) > 0:
            proxy_ip = random.choice(self.proxy_list[channel])
            expire_time = proxy_ip.get('expire_time', None)
            d1 = datetime.datetime.strptime(expire_time, '%Y-%m-%d %H:%M:%S')
            now_time = datetime.datetime.now()
            if now_time >= d1:
                self.delete_invalid_ip(proxy_ip, channel)
                result_ip = random.choice(self.proxy_list[channel])
            else:
                count = proxy_ip.get('use_count', 0)
                count += 1
                proxy_ip['use_count'] = count
                result_ip = proxy_ip
        return result_ip

    def delete_invalid_ip(self, proxy, channel):
        identifier = self.acquire_lock("get_proxy_ip_list")
        if proxy in self.proxy_list[channel]:
            self.proxy_list[channel].remove(proxy)
        ip_json = self.redis_helper.get_string("ip_list", None)
        if ip_json is not None:
            cache_proxy_list = json.loads(ip_json)
            for ip in cache_proxy_list:
                if ip.get("ip") == proxy.get("ip"):
                    cache_proxy_list.remove(ip)
                    break
            self.redis_helper.set_string("ip_list", json.dumps(cache_proxy_list, ensure_ascii=False))
        self.release_lock("get_proxy_ip_list", identifier)

    # 获取一个锁
    # lock_name：锁定名称
    # acquire_time: 客户端等待获取锁的时间
    # time_out: 锁的超时时间
    def acquire_lock(self, lock_name, acquire_time=10, time_out=10):
        """获取一个分布式锁"""
        identifier = str(uuid.uuid4())
        lock_name = "string:lock:" + lock_name
        lock_timeout = int(math.ceil(time_out))
        end = time.time() + acquire_time
        while time.time() < end:
            # 如果不存在这个锁则加锁并设置过期时间，避免死锁
            if self.redis_helper.set(lock_name, identifier, lock_timeout, True):
                return identifier
            time.sleep(0.001)
        return False

    # 释放一个锁
    def release_lock(self, lock_name, identifier):
        """通用的锁释放函数"""
        unlock_script = """
            if redis.call("get",KEYS[1]) == ARGV[1] then
                return redis.call("del",KEYS[1])
            else
                return 0
            end
            """
        if identifier is False:
            return False
        lockname = "string:lock:" + lock_name
        unlock = self.redis_helper.register_script(unlock_script)
        result = unlock(keys=[lockname], args=[identifier])
        if result:
            return True
        else:
            return False
